﻿
Imports osi.root.connector
Imports osi.root.template
Imports osi.root.formation
Imports osi.root.delegates
Imports osi.root.lock

Public MustInherit Class unique_map(Of KEY_T As IComparable(Of KEY_T), STORE_T, VALUE_T, HASH_SIZE As _int64)
    Private ReadOnly m As hashmap(Of KEY_T, STORE_T, HASH_SIZE) = Nothing
    Private l As duallock

    Public Sub New()
        m = New hashmap(Of KEY_T, STORE_T, HASH_SIZE)()
    End Sub

    Protected MustOverride Function store_value(ByVal i As STORE_T, ByRef o As VALUE_T) As Boolean
    Protected MustOverride Function value_store(ByVal i As VALUE_T) As STORE_T

    Public Function size() As Int64
        Return l.reader_locked(Function() m.size())
    End Function

    Private Function unlocked_erase(ByVal key As KEY_T, ByRef v As VALUE_T) As Boolean
        Dim it As hashmap(Of KEY_T, STORE_T, HASH_SIZE).iterator = Nothing
        it = m.find(key)
        If it = m.end() Then
            Return False
        Else
            store_value((+it).second, v)
            Return assert(m.erase(it))
        End If
    End Function

    Public Function [erase](ByVal key As KEY_T,
                            Optional ByRef v As VALUE_T = Nothing) As Boolean
        Dim t As VALUE_T = Nothing
        Return l.writer_locked(Function() As Boolean
                                   Return unlocked_erase(key, t)
                               End Function) AndAlso
               eva(v, t)
    End Function

    Public Function [erase](ByVal keys As vector(Of KEY_T),
                            Optional ByVal vs As vector(Of VALUE_T) = Nothing) As Boolean
        If keys Is Nothing Then
            Return False
        ElseIf keys.empty() Then
            Return True
        Else
            If Not vs Is Nothing Then
                vs.clear()
            End If
            Return l.writer_locked(Function() As Boolean
                                       Dim r As Boolean = False
                                       r = True
                                       For i As Int32 = 0 To keys.size() - 1
                                           Dim v As VALUE_T = Nothing
                                           If unlocked_erase(keys(i), v) Then
                                               If Not vs Is Nothing Then
                                                   vs.emplace_back(v)
                                               End If
                                           Else
                                               r = False
                                           End If
                                       Next
                                       Return r
                                   End Function)
        End If
    End Function

    Public Function [get](ByVal key As KEY_T, ByRef v As VALUE_T) As Boolean
        Dim v2 As VALUE_T = Nothing
        Return l.reader_locked(Function() As Boolean
                                   Dim it As hashmap(Of KEY_T, STORE_T, HASH_SIZE).iterator = Nothing
                                   it = m.find(key)
                                   If it <> m.end() Then
                                       Return store_value((+it).second, v2)
                                   Else
                                       Return False
                                   End If
                               End Function,
                               False) AndAlso
               eva(v, v2)
    End Function

    Public Function [get](ByVal key As KEY_T) As VALUE_T
        Dim o As VALUE_T = Nothing
        assert([get](key, o))
        Return o
    End Function

    Public Function [get](ByVal keys() As KEY_T, ByRef o As vector(Of VALUE_T)) As Boolean
        Dim r As Boolean = False
        r = True
        o.renew()
        For i As Int32 = 0 To array_size(keys) - 1
            Dim t As VALUE_T = Nothing
            If [get](keys(i), t) Then
                o.emplace_back(t)
            Else
                r = False
            End If
        Next
        Return r
    End Function

    Public Function [get](ByVal ParamArray names() As KEY_T) As vector(Of VALUE_T)
        Dim r As vector(Of VALUE_T) = Nothing
        assert([get](names, r))
        Return r
    End Function

    'v is IN and OUT parameter
    'v is always the value in the collection
    'return true if the value has been changed to v
    'otherwise v has been changed to the value in the collection
    Private Function get_set(ByVal k As KEY_T, ByRef v As VALUE_T) As Boolean
        Dim o As VALUE_T = Nothing
        Return eva(o, v) And
               l.writer_locked(Function() As Boolean
                                   Dim it As hashmap(Of KEY_T, STORE_T, HASH_SIZE).iterator = Nothing
                                   Dim r As VALUE_T = Nothing
                                   it = m.find(k)
                                   If it = m.end() OrElse Not store_value((+it).second, r) Then
                                       m.insert(k, value_store(o))
                                       Return True
                                   Else
                                       o = r
                                       Return False
                                   End If
                               End Function) And
               eva(v, o)
    End Function

    Public Function [set](ByVal key As KEY_T, ByVal v As VALUE_T) As Boolean
        Return get_set(key, v)
    End Function

    Public Function replace(ByVal key As KEY_T, ByVal v As VALUE_T) As Boolean
        Return l.writer_locked(Function() As Boolean
                                   m(key) = value_store(v)
                                   Return True
                               End Function)
    End Function

    Public Function exist(ByVal key As KEY_T) As Boolean
        Dim r As VALUE_T = Nothing
        Return [get](key, r)
    End Function

    Public Function generate(ByVal key As KEY_T, ByVal ctor As _do(Of VALUE_T)) As VALUE_T
        assert(Not ctor Is Nothing)
        Dim v As VALUE_T = Nothing
        If [get](key, v) Then
            Return v
        Else
            v = ctor()
            get_set(key, v)
            Return v
        End If
    End Function

    Public Function generate(ByVal key As KEY_T) As VALUE_T
        Return generate(key, AddressOf alloc(Of VALUE_T))
    End Function

    Public Function foreach(ByVal d As _do(Of KEY_T, VALUE_T, Boolean, Boolean)) As Boolean
        If d Is Nothing Then
            Return False
        Else
            Return l.reader_locked(Function() m.foreach(
                                       Function(ByRef x As KEY_T, ByRef y As STORE_T, ByRef c As Boolean) As Boolean
                                           Dim v As VALUE_T = Nothing
                                           If store_value(y, v) Then
                                               Return d(x, v, c)
                                           Else
                                               c = True
                                               Return True
                                           End If
                                       End Function))
        End If
    End Function

    Public Function foreach(ByVal d As _do(Of KEY_T, VALUE_T, Boolean)) As Boolean
        Return utils.foreach(AddressOf foreach, d)
    End Function

    Public Function foreach(ByVal d As _do(Of KEY_T, Boolean, Boolean)) As Boolean
        Return utils.foreach(Of KEY_T, VALUE_T)(AddressOf foreach, d)
    End Function

    Public Function foreach(ByVal d As _do(Of VALUE_T, Boolean, Boolean)) As Boolean
        Return utils.foreach(Of KEY_T, VALUE_T)(AddressOf foreach, d)
    End Function

    Public Function foreach(ByVal d As _do(Of KEY_T, Boolean)) As Boolean
        Return utils.foreach(AddressOf foreach, d)
    End Function

    Public Function foreach(ByVal d As _do(Of VALUE_T, Boolean)) As Boolean
        Return utils.foreach(AddressOf foreach, d)
    End Function

    Public Function foreach(ByVal d As void(Of KEY_T, VALUE_T, Boolean)) As Boolean
        Return utils.foreach(AddressOf foreach, d)
    End Function

    Public Function foreach(ByVal d As void(Of KEY_T, VALUE_T)) As Boolean
        Return utils.foreach(AddressOf foreach, d)
    End Function
End Class
